# 常用中间件 body_parser 实现解析
30 秒速记
- 请求体在
Node.js里是一段字节流,req.body是解析中间件挂上去的,不是天生就有 Express 4.16+内置express.json()/express.urlencoded(),底层就是body-parser- 解析链路:收字节 → 按
Content-Encoding解压 → 按charset解码 → 按Content-Type解析 - 默认上限
100kb,超了413;JSON写错400;类型不匹配就跳过,req.body拿不到值 - 解析成功 ≠ 字段合法,还要做
schema校验;webhook验签要用verify留住原始字节
body-parser 干的事就是把请求体这段字节流读完、解压、解码,再按类型解析成对象挂到 req.body 上。 很多人以为 req.body 是 Express 自带的,其实不注册解析中间件它就是空的,Express 4 里是 undefined,用了 body-parser 1.x 但类型不匹配时是 {}。现在 Express 4.16 以后直接用 express.json() 就行,不用单独装包。实际项目里我最在意的是三件事:限制大小、出错要返回明确的 4xx、解析出来的数据还得过一遍校验。
理解「常用中间件 body_parser 实现解析」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。
工程中还要验证退出码、资源清理和异常输入。属于 middleware 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。
HTTP 请求到了 Node.js 里,req 是一个可读流,请求体要自己监听 data 和 end 事件一段一段收。body-parser 就是把这件事封装成中间件,收完以后解析好挂到 req.body,后面的路由直接用。
在 Express 里一般这样写:
const express = require('express');
const app = express();
app.use(express.json({ limit: '100kb' })); // application/json
app.use(express.urlencoded({ extended: false })); // 普通表单
app.post('/users', (req, res) => {
res.json({ name: req.body.name });
});
// 解析失败统一兜底
app.use((err, req, res, next) => {
if (err.type === 'entity.parse.failed') return res.status(400).json({ msg: 'JSON 格式错误' });
if (err.type === 'entity.too.large') return res.status(413).json({ msg: '请求体过大' });
next(err);
});
app.listen(3000);
几个版本差异要分清:Express 4.16 之前必须单独装 body-parser;4.16 起 express.json() 和 express.urlencoded() 内置,本质是同一套代码。Express 5 配套的是 body-parser 2.x,urlencoded 的 extended 默认改成了 false,而且没有任何解析器匹配时 req.body 是 undefined,不再是 {},老代码里 if (req.body.xxx) 这种写法升级时要留意。
文件上传用的 multipart/form-data 不归 body-parser 管,要用 multer、busboy 这类专门的库。
面试官追问
追问 1路由里 req.body 一直是 undefined,最先查什么?
先看有没有注册 express.json(),以及它是不是在路由之前 app.use 的。顺序对了还不行,就看请求头的 Content-Type,前端发的不是 application/json 的话,json() 会直接跳过不解析。
追问 2前端传了一个 200KB 的 JSON,后端直接报 413,为什么?
body-parser 默认 limit 是 100kb,超了就返回 413 Payload Too Large。按接口放开就行,比如 express.json({ limit: '1mb' }) 只挂在导入接口上,别全局一刀切调大。
追问 3前端发了个格式错误的 JSON,服务返回了一大段 HTML 报错页,怎么改?
body-parser 解析失败会 next(err),带上 err.status = 400 和 err.type = 'entity.parse.failed',没写错误处理中间件就会走 Express 默认的 HTML 错误页。补一个四参数的错误中间件,按 err.type 返回统一的 JSON 错误。
追问 4接 Stripe 这类 webhook 要验签,可是 req.body 已经被解析成对象了怎么办?
签名是对原始字节算的,JSON.stringify(req.body) 重新序列化出来的字符串大概率对不上。用 express.json({ verify: (req, res, buf) => { req.rawBody = buf } }) 把原始 Buffer 留下来,或者这个路由单独用 express.raw({ type: 'application/json' })。
追问 5JSON 能解析成功,是不是就可以直接存库了?
不行,解析只保证语法对,{"age": "abc"} 一样能解析。字段类型、必填、长度都要用 zod、joi 这类库校验,多余字段也要明确是丢弃还是报错。
# 写在前面
30 秒速记
- 两行覆盖大部分场景:
bodyParser.json()解JSON,bodyParser.urlencoded({ extended: false })解表单 - 每个解析器只认自己的
Content-Type,不匹配就放行,不报错也不解析 - 必须注册在路由之前,中间件按
app.use的顺序执行 extended: false用querystring,true用qs,能解析a[b]=1这种嵌套Express 4.16+直接写express.json(),不用再装包
body-parser 是 Express 里专门解析请求体的中间件,json 和 urlencoded 两行就能覆盖绝大多数接口。 它的工作方式是看请求的 Content-Type,匹配上了才去读流、解析,挂到 req.body,没匹配上就直接 next()。所以前端发 JSON、后端只注册了 urlencoded,req.body 就是空的,这锅不在前端。新项目我一般直接用 Express 内置的 express.json(),效果一样。
body-parser是非常常用的一个express中间件,作用是对http请求体进行解析。使用非常简单,以下两行代码已经覆盖了大部分的使用场景。
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
本文从简单的例子出发,探究body-parser的内部实现。至于body-parser如何使用,感兴趣的同学可以参考官方文档。
面试官追问
追问 1前端发 JSON 到 /login,后端只注册了 urlencoded,req.body 是空的,是前端没传吗?
不是,数据传到了,只是没有解析器认领它。urlencoded() 只处理 application/x-www-form-urlencoded,JSON 请求被跳过了。补上 express.json() 就行。
追问 2把 app.use(bodyParser.json()) 写在路由定义后面,会怎样?
请求按注册顺序走中间件,先命中路由处理函数,那时候还没人解析请求体,req.body 自然是空的。解析中间件要放在依赖它的路由之前。
追问 3表单要提交 user[name]=tom&user[age]=18,extended: false 能解析成嵌套对象吗?
不能,false 用的是 querystring,会得到 { 'user[name]': 'tom', ... } 这样的平铺键。要嵌套就改成 extended: true 用 qs,但它会带来深度和数组的复杂输入,记得控制 parameterLimit 和深度,结构复杂的数据我更倾向让前端直接发 JSON。
追问 4全局挂解析器和按路由挂,怎么选?
大部分接口都收 JSON 就全局挂,省事。只有个别接口要大 limit、或者 webhook 需要原始字节,就单独在那个路由上挂 express.raw() 或者 express.json({ limit: '5mb' }),注意别让全局的解析器先把流读掉了。
